#include "AnimationSystem.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"

#include "../TinyXML/tinyxml.h"

#include "../SGD Wrappers/SGD_EventManager.h"
#include "../SGD Wrappers/SGD_Event.h"

#include "GameObject.h"

AnimationSystem* AnimationSystem::s_pInstance = nullptr;

//Get Instance
//Allocate ONE instance of the singleton object
AnimationSystem* AnimationSystem::GetInstance()
{
	if (s_pInstance == nullptr)
		s_pInstance = new AnimationSystem;

	return s_pInstance;
}

//DeleteInstance
//deallocate the singleton object
void AnimationSystem::DeleteInstance()
{
	delete s_pInstance;
	s_pInstance = nullptr;
}

//Initialize
//create the frame and animations for debugging
void AnimationSystem::Initialize()
{

}

void AnimationSystem::Update(AnimationTimeStamp& ATS, float elapsedTime, GameObject* eventSender)
{
	if (ATS.GetCurrAnimation() == "" || loaded.find(ATS.GetCurrAnimation()) == loaded.end())
	{
		return;
	}

		ATS.SetTimeOnFrame(ATS.GetTimeOnFrame() + elapsedTime);

		vector<Frame> temp = loaded[ATS.GetCurrAnimation()].GetFrames();
		Frame* currFrame = &loaded[ATS.GetCurrAnimation()].GetFrames()[ATS.GetCurrFrame()];
		float currFrameDuration = currFrame->GetDuration();
		Animation* currAnimation = &loaded[ATS.GetCurrAnimation()];

		vector<Frame>* vecOfFrames = &loaded[ATS.GetCurrAnimation()].GetFrames();
		Animation* tempAnim = &loaded[ATS.GetCurrAnimation()];

		if (ATS.GetFacing() < 0)
			currFrame->SetFacing(true);
		else if (ATS.GetFacing() > 0)
			currFrame->SetFacing(false);

		if (ATS.GetTimeOnFrame() > currFrameDuration && currAnimation->GetIsPlaying() == true)
		{
			ATS.SetTimeOnFrame(ATS.GetTimeOnFrame() - currFrameDuration);
			ATS.SetCurrFrame(ATS.GetCurrFrame() + 1);

			if ((float)ATS.GetCurrFrame() > vecOfFrames->size() - 1 &&
				currAnimation->GetIsLooping() == false)
			{
				currAnimation->SetIsPlaying(false);
				StopAnimation(ATS);
			}
			else if ((float)ATS.GetCurrFrame() > vecOfFrames->size() - 1 &&
				currAnimation->GetIsLooping() == true)
			{
				ATS.SetCurrFrame(0);
			}

			if (currFrame->GetTheEvent() != "")
			{
				SGD::Event* theEvent = new SGD::Event(currFrame->GetTheEvent().c_str(), nullptr, eventSender);

				theEvent->QueueEvent();
			}
		}
	
	turnedAround = false;
}

void AnimationSystem::Render(SGD::Point renderPosition, AnimationTimeStamp& ATS)
{
	if (ATS.GetCurrAnimation() == "" || loaded.find(ATS.GetCurrAnimation()) == loaded.end())
	{
		return;
	}
	{
		//Shortcuts
		Animation currAnimation = loaded[ATS.GetCurrAnimation()];
		vector<Frame> vecOfFrames = loaded[ATS.GetCurrAnimation()].GetFrames();
		Frame currFrame = vecOfFrames[ATS.GetCurrFrame()];
		SGD::Rectangle theRenderRect = loaded[ATS.GetCurrAnimation()].GetFrames()[ATS.GetCurrFrame()].GetRenderRect();
		SGD::Point renderPoint = { renderPosition.x - (currFrame.GetAnchorPoint().x), renderPosition.y - (currFrame.GetAnchorPoint().y) };


		SGD::GraphicsManager* gfx = SGD::GraphicsManager::GetInstance();

		SGD::Rectangle collisionRect = { SGD::Point(renderPoint.x + currFrame.GetCollisionRect().left, renderPoint.y + currFrame.GetCollisionRect().top),
			SGD::Size(currFrame.GetCollisionRect().ComputeWidth(), currFrame.GetCollisionRect().ComputeHeight()) };

		SGD::Rectangle activeRect = { SGD::Point(renderPoint.x + currFrame.GetActiveRect().left, renderPoint.y + currFrame.GetActiveRect().top),
			SGD::Size(currFrame.GetActiveRect().ComputeWidth(), currFrame.GetActiveRect().ComputeHeight()) };

		//Render the current frame of the animation
		if (currAnimation.GetIsPlaying() == true && ATS.GetFacing() > 0)
		{
			//render the renderRectangle
			if (ATS.GetFlashOn())
				gfx->DrawTextureSection(currAnimation.GetImageID(), renderPoint, theRenderRect, 0.0f, {}, { 255, 255, 0, 0 }, { 1, 1 });
			else
				gfx->DrawTextureSection(currAnimation.GetImageID(), renderPoint, theRenderRect, 0.0f, {}, {}, { 1, 1 });


#if 1
			////render the collision rectangle if you are in DEBUG mode
			//gfx->DrawRectangle(collisionRect, { 150, 255, 255, 0 });

			////render the anchorPoint if you are in DEBUG mode
			//gfx->DrawRectangle({ renderPosition.x - 2, renderPosition.y - 2, renderPosition.x + 2, renderPosition.y + 2 }, { 255, 255, 255, 255 });

			////render the active rectangle if you are in DEBUG mode
			//gfx->DrawRectangle(activeRect, { 150, 255, 0, 0 });


#endif
		}
		else if (currAnimation.GetIsPlaying() == true && ATS.GetFacing() < 0)
		{
			//render the renderRectangle
			if (ATS.GetFlashOn())
				gfx->DrawTextureSection(currAnimation.GetImageID(), { renderPoint.x + theRenderRect.ComputeWidth(), renderPoint.y }, theRenderRect, 0.0f, {}, { 255, 255, 0, 0 }, { -1, 1 });
			else
				gfx->DrawTextureSection(currAnimation.GetImageID(), { renderPoint.x + theRenderRect.ComputeWidth(), renderPoint.y }, theRenderRect, 0.0f, {}, {}, { -1, 1 });

#if 1
			//render the collision rectangle if you are in DEBUG mode
			//gfx->DrawRectangle(collisionRect, { 150, 255, 255, 0 });

			////render the anchorPoint if you are in DEBUG mode
			//gfx->DrawRectangle({ renderPosition.x - 2, renderPosition.y - 2, renderPosition.x + 2, renderPosition.y + 2 }, { 255, 255, 255, 255 });

			////render the active rectangle if you are in DEBUG mode
			//gfx->DrawRectangle(activeRect, { 150, 255, 0, 0 });
#endif
		}
	}
}

bool AnimationSystem::GetAnimationInfoFromFile(char* theFile)
{
	string animName;
	bool looping;
	vector<Frame> vecOfFrames;
	string path = "resource\\Animations\\";
	path += theFile;
	const char* docPath = path.c_str();

	for (unsigned int i = 0; i < loadedFiles.size(); i++)
	{
		if (path == loadedFiles[i])
		{
			return false;
		}
	}

	loadedFiles.push_back(path);

	//Create the XML Document
	TiXmlDocument doc(docPath);

	//Attempt to load
	if (doc.LoadFile() == false)
		return false;

	TiXmlHandle docHandle(&doc);
	TiXmlHandle rootHandle(0);

	//Access the root element
	TiXmlElement* elemPointer;
	elemPointer = doc.RootElement();
	if (elemPointer == nullptr)
		return false;

	TiXmlElement* element = elemPointer->FirstChildElement("FileName");
	TiXmlElement* animElement;

	const char* fileName = element->GetText();

	path = "resource/Graphics/";
	path += fileName;

	const char* fullPath = path.c_str();

	images.push_back(SGD::GraphicsManager::GetInstance()->LoadTexture(fullPath));

	animElement = element->NextSiblingElement("Animations");
	animElement = animElement->FirstChildElement("Animation");


	while (animElement != nullptr)
	{
		animName = animElement->Attribute("Name");
		looping = !!(atoi(animElement->Attribute("isLooping")));


		element = animElement->FirstChildElement("Frames");
		element = element->FirstChildElement("Frame");

		while (element != nullptr)
		{
			Frame currFrame;

			int temp;
			float ancPt[2];
			float offset[2];
			temp = atoi(element->Attribute("FrameNumber"));
			ancPt[0] = static_cast<float>(atof(element->Attribute("AnchorPointX")));
			ancPt[1] = static_cast<float>(atof(element->Attribute("AnchorPointY")));
			offset[0] = static_cast<float>(atof(element->Attribute("WeaponOffsetX")));
			offset[1] = static_cast<float>(atof(element->Attribute("WeaponOffsetY")));

			float time = static_cast<float>(atof(element->Attribute("Duration")));
			currFrame.SetAnchorPoint(SGD::Point(ancPt[0], ancPt[1]));
			currFrame.SetWeaponOffset(SGD::Point(offset[0], offset[1]));
			currFrame.SetDuration(time);

			string trigger = element->Attribute("Trigger");
			currFrame.SetTheEvent(trigger);

			TiXmlElement* frameElem;

			//begin render rect
			frameElem = element->FirstChildElement("RenderRect");

			SGD::Rectangle rendRect;
			rendRect.left = static_cast<float>(atof(frameElem->Attribute("x")));
			rendRect.top = static_cast<float>(atof(frameElem->Attribute("y")));
			rendRect.right = rendRect.left + static_cast<float>(atof(frameElem->Attribute("width")));
			rendRect.bottom = rendRect.top + static_cast<float>(atof(frameElem->Attribute("height")));

			//set the current frame's render rect
			currFrame.SetRenderRect(rendRect);

			frameElem = frameElem->NextSiblingElement();

			//begin active rect
			SGD::Rectangle actRect;

			actRect.left = static_cast<float>(atof(frameElem->Attribute("x")));
			actRect.top = static_cast<float>(atof(frameElem->Attribute("y")));
			actRect.right = actRect.left + static_cast<float>(atof(frameElem->Attribute("width")));
			actRect.bottom = actRect.top + static_cast<float>(atof(frameElem->Attribute("height")));

			//set the current Frame's active rect
			currFrame.SetActiveRect(actRect);

			frameElem = frameElem->NextSiblingElement();

			//begin collision rect
			SGD::Rectangle colRect;

			colRect.left = static_cast<float>(atof(frameElem->Attribute("x")));
			colRect.top = static_cast<float>(atof(frameElem->Attribute("y")));
			colRect.right = colRect.left + static_cast<float>(atof(frameElem->Attribute("width")));
			colRect.bottom = colRect.top + static_cast<float>(atof(frameElem->Attribute("height")));

			//set the current frame's 
			currFrame.SetCollisionRect(colRect);

			vecOfFrames.push_back(currFrame);

			element = element->NextSiblingElement();
		}

		AddAnimation(animName, vecOfFrames, images[images.size() - 1]);
		vecOfFrames.clear();
		animElement = animElement->NextSiblingElement();
	}
	return true;
}

//StartLooping
//Allow the current animation to loop
void AnimationSystem::StartLooping(AnimationTimeStamp& ATS)
{
	if (loaded.find(ATS.GetCurrAnimation()) == loaded.end())
	{
		return;
	}
	else if (loaded[ATS.GetCurrAnimation()].GetIsPlaying() == true)
		loaded[ATS.GetCurrAnimation()].SetIsLooping(true);
}

void AnimationSystem::StartPlaying(AnimationTimeStamp& ATS)
{
	//StopAnimation(ATS);
	//Animation blahg = loaded[ATS.GetCurrAnimation()];
	//bool blah = loaded[ATS.GetCurrAnimation()].GetIsPlaying();
	if (loaded.find(ATS.GetCurrAnimation()) == loaded.end())
	{
		return;
	}
	else if (loaded[ATS.GetCurrAnimation()].GetIsPlaying() == false)
	{
		StopAnimation(ATS);
		loaded[ATS.GetCurrAnimation()].SetIsPlaying(true);
		loaded[ATS.GetCurrAnimation()].SetIsLooping(false);
	}
}

void AnimationSystem::StopAnimation(AnimationTimeStamp& ATS)
{
	if (loaded.find(ATS.GetCurrAnimation()) == loaded.end())
	{
		return;
	}
	else
	{
		ATS.SetCurrFrame(0);
		ATS.SetTimeOnFrame(0);
		loaded[ATS.GetCurrAnimation()].SetIsPlaying(false);
	}
}

void AnimationSystem::AddAnimation(string name, vector<Frame> anim, SGD::HTexture img)
{
	loaded[name].SetFrames(anim);
	loaded[name].SetImageID(img);
	loaded[name].SetIsPlaying(false);
}

void AnimationSystem::UnloadTexture()
{
	for (unsigned int i = 0; i < images.size(); i++)
		SGD::GraphicsManager::GetInstance()->UnloadTexture(images[i]);
}

SGD::Rectangle AnimationSystem::GetCollisionRectangle(AnimationTimeStamp& ATS)
{
	if (loaded.find(ATS.GetCurrAnimation()) == loaded.end())
	{
		return SGD::Rectangle(0, 0, 0, 0);
	}
	return loaded[ATS.GetCurrAnimation()].GetFrames()[ATS.GetCurrFrame()].GetCollisionRect();
}

bool AnimationSystem::Contains(string animation)
{
	if (animation == "" || loaded.find(animation) == loaded.end())
		return false;
	return true;
}